package cn.com.zcode.core.cxf.client;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.log4j.Logger;

public class ConvertUtil {
	private static Logger logger = Logger.getLogger(ConvertUtil.class);
	private static Convert[] converts = new Convert[] { new BaseConvert(), new MapConvert() };

	/**
	 * Convert a object to a new Class
	 * 
	 * @param entity
	 *            The src object which need to be converted
	 * @param newClass
	 *            the Class type of new Object.
	 * @param inFields
	 *            include Field name of the src object.
	 * @return
	 * @throws Exception
	 */
	public static <T> T convert(Object entity, Class<T> newClass, String... inFields) throws Exception {
		T targetObject = null;
		if (newClass != null && entity != null) {
			try {
				targetObject = newClass.cast(entity);
			} catch (ClassCastException e) {
				// Get the target fields.
				Set<Field> set = new HashSet<Field>();
				if (inFields == null || inFields.length < 1) {
					Field[] fields = newClass.getDeclaredFields();
					for (Field field : fields) {
						set.add(field);
					}
				} else {
					for (String string : inFields) {
						Field field = newClass.getDeclaredField(string);
						set.add(field);
					}
				}
				// Fill the target fields.
				targetObject = newClass.newInstance();
				for (Field newField : set) {
					Object value = invokeGetValue(entity, newField);
					invokeSetValue(targetObject, newField, value);
				}
				logger.debug("Convert :" + entity + " -> " + targetObject);
			}
		}
		return targetObject;

	}

	/**
	 * @param target
	 * @param fieldName
	 * @param newFieldType
	 * @param value
	 * @return
	 */
	public static boolean invokeSetValue(Object target, Field newField, Object value) {
		String newFieldName = newField.getName();
		Class<?> newFieldType = newField.getType();

		String setMethodName = "set" + (newFieldName.substring(0, 1).toUpperCase()) + newFieldName.substring(1);
		Class<?> zclass = target.getClass();
		try {
			Method method = zclass.getDeclaredMethod(setMethodName, newFieldType);
			method.invoke(target, value);
			return true;
		} catch (Exception e) {
			logger.debug("can not set " + value.getClass().getName() + " value \"" + value + "\" to " + newFieldType.getName() + " field  [" + newFieldName + "]");

		}
		return false;
	}

	/**
	 * @param entity
	 * @param fieldName
	 * @return
	 * @throws InvocationTargetException
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @throws Exception
	 */
	public static Object invokeGetValue(Object entity, Field newField) {
		Object obj = null;
		String fieldName = newField.getName();
		Class<?> elass = entity.getClass();
		String getMethodName = "get" + (fieldName.substring(0, 1).toUpperCase()) + fieldName.substring(1);
		Method method = null;

		try {
			method = elass.getDeclaredMethod(getMethodName);
		} catch (Exception e) {
			logger.debug("No such method:" + getMethodName);
			if (boolean.class.equals(elass.getClass())) {
				String getMethodName2 = "is" + (fieldName.substring(0, 1).toUpperCase()) + fieldName.substring(1);
				try {
					method = elass.getDeclaredMethod(getMethodName2);
				} catch (Exception e1) {
					logger.debug("No such method:" + getMethodName2);
				}
			}
		}
		if (method != null) {
			try {
				obj = method.invoke(entity);
				Type ptype = newField.getGenericType();
				for (Convert con : ConvertUtil.converts) {
					obj = con.format(obj, ptype);
				}
				ConvertAdapter typeconvert = newField.getAnnotation(ConvertAdapter.class);
				if (typeconvert != null) {
					Convert convert = typeconvert.value().newInstance();
					obj = convert.format(obj, ptype);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return obj;
	}

	public static Object parse2WsObject(Object obj) {
		for (Convert con : ConvertUtil.converts) {
			obj = con.parse(obj);
		}
		return obj;
	}

	public static void main(String[] args) throws Exception {
		Test ws = new Test();

		Test2 obj = convert(ws, Test2.class);
		System.out.println(obj);
	}

	public static class Test {
		private Integer abc;
		private List<Integer> list = new ArrayList<Integer>();

		public Test() {
			super();
			this.abc = 10;
			this.list.add(1);
			this.list.add(2);
			this.list.add(3);
		}

		public Integer getAbc() {
			return abc;
		}

		public void setAbc(Integer abc) {
			this.abc = abc;
		}

		public List<Integer> getList() {
			return list;
		}

		public void setList(List<Integer> list) {
			this.list = list;
		}

		@Override
		public String toString() {
			return "Test [abc=" + abc + ", list=" + list + "]";
		}

	}

	public static class Test2 {
		private int abc;
		private List<Integer> list = new ArrayList<Integer>();

		public int getAbc() {
			return abc;
		}

		public void setAbc(int abc) {
			this.abc = abc;
		}

		public List<Integer> getList() {

			return list;
		}

		public void setList(List<Integer> list) {
			this.list = list;
		}

		@Override
		public String toString() {
			return "Test2 [abc=" + abc + ", list=" + list + "]";
		}

	}

}
